package org.rency.crawler.fetch.service.grade.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.rency.crawler.common.CrawlerConstants;
import org.rency.crawler.domain.FetchExtension;
import org.rency.crawler.domain.queue.FetchQueue;
import org.rency.crawler.fetch.helper.FetchCarrier;
import org.rency.crawler.fetch.helper.FetchHelper;
import org.rency.crawler.fetch.service.grade.GradeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * OCIP评分
 * <p>OCIP策略(Online Page Importance Computation)字面含义为“在线页面重要性计算”，算是一种改进的PageRank算法。</p>
 * <p>算法开始前，每个互联网页面被赋予相同的数值，每当下载了某个页面P后，P将自己拥有的数值平均分配给页面中包含的链接，同时清空自己的数值。</p>
 * <p>对于待抓取URL队列中的网页，根据其手头数值大小排序，优先下载数值较大的网页</p>
 * @author: Created by user_rcy@163.com on 2017/1/16.
 * @version: V1.0.0
 * @since JDK 1.8
 */
@Service("ocipGradeService")
public class OCIPGradeServiceImpl implements GradeService {

    private static final Logger logger = LoggerFactory.getLogger(OCIPGradeServiceImpl.class);

    @Override
    public void grade(FetchCarrier carrier) {
        double averageWeight = this.getAverageWeight(carrier);
        this.setFetchWeight(carrier,averageWeight);
        //TODO 因设置权重时值是一样的，故无需再排序
        //this.sortFetchCollection(carrier);
    }

    /**
     * 获取权重
     * @param carrier
     * @return
     */
    private double getAverageWeight(FetchCarrier carrier){
        String fetchUri = carrier.getRequest().getFetchUri();
        String fetchNo = carrier.getRequest().getFetchNo();
        //全局默认权重
        double defaultWeight = CrawlerConstants.DEFAULT_PAGE_WEIGHT;
        //队列大小
        int queueSize = CollectionUtils.isEmpty(carrier.getFetchQueueCollection()) ? 0 : carrier.getFetchQueueCollection().size();
        //计算后平均权重
        return FetchHelper.calculateWeightAverage(fetchNo,fetchUri,carrier.getExtension().getFetchWeight(),queueSize,defaultWeight);
    }

    /**
     * 设置抓取队列权重
     * @param carrier
     * @param value
     */
    private void setFetchWeight(FetchCarrier carrier,double value){
        logger.debug("设置新种子权重:[{}-{}],权重:{}.",carrier.getRequest().getFetchNo(),carrier.getRequest().getFetchUri(),value);
        if(CollectionUtils.isEmpty(carrier.getFetchQueueCollection())){
            return;
        }
        Iterator<FetchQueue> iterator = carrier.getFetchQueueCollection().iterator();
        FetchExtension extension = null;
        while(iterator.hasNext()){
            FetchQueue domain = iterator.next();
            //将当前目标子目标队列中移除
            if(StringUtils.equals(carrier.getRequest().getFetchNo(),domain.getFetchNo())){
                iterator.remove();
                continue;
            }
            extension = domain.getExtension();
            if(extension == null){
                domain.setExtension(new FetchExtension());
                extension = domain.getExtension();
            }
            extension.setFetchWeight(value);
        }

        //将当前目标权重 置零
        carrier.getExtension().setFetchWeight(0.0);
    }

    /**
     * 对待抓取集合按照权重排序
     * @param carrier
     */
    private void sortFetchCollection(FetchCarrier carrier){
        if(CollectionUtils.isEmpty(carrier.getFetchQueueCollection())){
            return;
        }
        List<FetchQueue> setList = new ArrayList<>(carrier.getFetchQueueCollection());
        Collections.sort(setList, new Comparator<FetchQueue>() {
            @Override
            public int compare(FetchQueue o1, FetchQueue o2) {
                if(o1.getExtension().getFetchWeight().compareTo(o2.getExtension().getFetchWeight()) < 0){
                    return -1;
                }
                if(o1.getExtension().getFetchWeight().compareTo(o2.getExtension().getFetchWeight()) > 0){
                    return 1;
                }
                return 0;
            }
        });
        carrier.setFetchQueueCollection(setList);
        setList.clear();
    }
}
